package com.gitee.srurl.core.api.impl.store;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gitee.srurl.core.annotation.ApiImpl;
import com.gitee.srurl.core.api.IDataStore;
import com.gitee.srurl.core.constant.ApiTypeConstant;
import com.gitee.srurl.core.domain.SucUrlRecordDomain;
import com.gitee.srurl.core.dto.DataModel;
import com.gitee.srurl.core.dto.RefreshRemainRecordDTO;
import com.gitee.srurl.core.dto.result.ShortUrlResultDTO;
import com.gitee.srurl.core.exception.ServiceException;
import com.gitee.srurl.core.service.ISucUrlRecordService;
import com.gitee.srurl.core.util.DateUtil;
import com.gitee.srurl.core.util.UniqueIdUtil;
import com.gitee.srurl.core.util.login.LoginHelper;
import com.gitee.srurl.core.util.login.OperationUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * mysql 存储
 *
 * @author zxw
 * @date 2023/4/19 22:50
 */
@Slf4j
@Component
@ApiImpl(ApiTypeConstant.DataStore.MYSQL)
public class MysqlStore implements IDataStore {

    @Autowired
    private ISucUrlRecordService urlRecordService;

    @Override
    public boolean save(DataModel dataModel) {

        this.dataModelCheckAndSetDefault(dataModel);
        LocalDateTime now = LocalDateTime.now();
        OperationUser loginUser = LoginHelper.getLoginUserWithDefault();
        SucUrlRecordDomain urlRecordDomain = SucUrlRecordDomain.builder()
                .id(UniqueIdUtil.nextId())
                .originUrl(dataModel.getOriginUrl())
                .shortUrl(dataModel.getShortUrl())
                .shortId(dataModel.getShortKey())
                .times(dataModel.getTimes())
                .remainTimes(dataModel.getTimes())
                .expireTime(dataModel.getExpireTime())
                .disable("0")
                .createId(loginUser.getUserId())
                .createTime(now)
                .updateId(loginUser.getUserId())
                .updateTime(now)
                .build();

        return urlRecordService.save(urlRecordDomain);
    }

    @Override
    public boolean update(DataModel dataModel) {

        this.dataModelCheckAndSetDefault(dataModel);
        OperationUser loginUser = LoginHelper.getLoginUserWithDefault();
        LocalDateTime now = LocalDateTime.now();
        // 查询历史记录
        LambdaQueryWrapper<SucUrlRecordDomain> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SucUrlRecordDomain::getShortId, dataModel.getShortKey())
                .eq(SucUrlRecordDomain::getDisable, "0");
        SucUrlRecordDomain urlRecordDomain = urlRecordService.getOne(queryWrapper);
        if (urlRecordDomain == null) {
            log.info("shortId:{},请求续期，查询不到激活的历史记录", dataModel.getShortKey());
            return false;
        }

        if (dataModel.getUpdateTimes()) {
            urlRecordDomain.setTimes(dataModel.getTimes());
            urlRecordDomain.setRemainTimes(dataModel.getTimes());
        }

        urlRecordDomain.setExpireTime(dataModel.getExpireTime());
        urlRecordDomain.setUpdateId(loginUser.getUserId());
        urlRecordDomain.setUpdateTime(now);

        return urlRecordService.updateById(urlRecordDomain);
    }

    @Override
    public ShortUrlResultDTO peggingOriginUrl(String shortId) {

        if (StrUtil.isBlank(shortId)) {
            return null;
        }
        SucUrlRecordDomain recordDomain = getShortUrlRecordByShortId(shortId);
        if (recordDomain != null) {
            return ShortUrlResultDTO.builder()
                    .originUrl(recordDomain.getOriginUrl())
                    .shortUrl(recordDomain.getShortUrl())
                    .times(recordDomain.getTimes())
                    .remainTimes(recordDomain.getRemainTimes())
                    .expireTime(recordDomain.getExpireTime())
                    .disabled(!StrUtil.equals(recordDomain.getDisable(), "0"))
                    .success(true)
                    .createId(recordDomain.getCreateId())
                    .build();
        }
        return null;
    }

    private SucUrlRecordDomain getShortUrlRecordByShortId(String shortId) {
        LambdaQueryWrapper<SucUrlRecordDomain> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SucUrlRecordDomain::getShortId, shortId)
                .eq(SucUrlRecordDomain::getDisable, "0");

        return urlRecordService.getOne(queryWrapper);
    }

    @Override
    public ShortUrlResultDTO shortUrlRemainingData(String shortUrl) {

        SucUrlRecordDomain urlRecordDomain = this.getShortUrlRecordByShortId(shortUrl);
        if (urlRecordDomain == null) {
            throw new ServiceException("短链不存在或已禁用！");
        }

        return ShortUrlResultDTO.builder()
                .originUrl(urlRecordDomain.getOriginUrl())
                .shortUrl(urlRecordDomain.getShortUrl())
                .expireTime(urlRecordDomain.getExpireTime())
                .times(urlRecordDomain.getTimes())
                .remainTimes(urlRecordDomain.getRemainTimes())
                .build();
    }

    @Override
    public boolean shortUrlExistIfEffectivity(String shortUrl) {

        LambdaQueryWrapper<SucUrlRecordDomain> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SucUrlRecordDomain::getShortUrl, shortUrl)
                .gt(SucUrlRecordDomain::getRemainTimes, 0)
                // 不需要等于 因为即使等于下一毫秒就过期了
                .lt(SucUrlRecordDomain::getExpireTime, DateTime.now());

        return urlRecordService.count(queryWrapper) > 0;
    }

    @Override
    public ShortUrlResultDTO queryByOriginUrl(String originUrl) {

        if (StrUtil.isBlank(originUrl)) {
            return null;
        }

        LambdaQueryWrapper<SucUrlRecordDomain> queryWrapper = new LambdaQueryWrapper<>();
        // 说不定之前设置的是每次返回新连接 但是后面设置相同url返回同一个链接 所以有可能存在多个的情况
        // 如果这种情况存在数据很多 这边可能要另外处理了 不然一个list数据很大 正常设置了就不会变 数据不会多
        queryWrapper.eq(SucUrlRecordDomain::getOriginUrl, originUrl)
                .orderByDesc(SucUrlRecordDomain::getCreateTime);

        List<SucUrlRecordDomain> urlRecordDomainList = urlRecordService.list(queryWrapper);

        if (CollectionUtil.isEmpty(urlRecordDomainList)) {
            return null;
        }

        long count = urlRecordDomainList.stream().filter(f -> StrUtil.equals(f.getDisable(), "1")).count();
        SucUrlRecordDomain recordDomain = urlRecordDomainList.get(0);
        return ShortUrlResultDTO.builder()
                .originUrl(recordDomain.getOriginUrl())
                .shortUrl(recordDomain.getShortUrl())
                .times(recordDomain.getTimes())
                .remainTimes(recordDomain.getRemainTimes())
                .expireTime(recordDomain.getExpireTime())
                .disabled(count > 0)
                .success(true)
                .build();
    }

    @Override
    public long getGenerateNum(Date startDate, Date endDate) {

        LambdaQueryWrapper<SucUrlRecordDomain> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.between(startDate != null && endDate != null,
                SucUrlRecordDomain::getCreateTime, startDate, endDate);
        return urlRecordService.count(queryWrapper);
    }

    @Override
    public List<RefreshRemainRecordDTO> getRecentOneDayHadVisitRecord() {

        List<SucUrlRecordDomain> urlRecordDomainList = urlRecordService.getRecentOneDayHadVisitRecordNonUnlimited();
        List<RefreshRemainRecordDTO> refreshRemainRecordDTOList = new ArrayList<>();
        for (SucUrlRecordDomain sucUrlRecordDomain : urlRecordDomainList) {

            RefreshRemainRecordDTO refreshRemainRecordDTO = new RefreshRemainRecordDTO();
            refreshRemainRecordDTO.setId(sucUrlRecordDomain.getId());
            refreshRemainRecordDTO.setShortId(sucUrlRecordDomain.getShortId());
            refreshRemainRecordDTO.setTimes(sucUrlRecordDomain.getTimes());
            refreshRemainRecordDTO.setRemainTimes(sucUrlRecordDomain.getRemainTimes());

            refreshRemainRecordDTOList.add(refreshRemainRecordDTO);
        }

        return refreshRemainRecordDTOList;
    }

    @Override
    public void refreshRecordRemainTimes(List<RefreshRemainRecordDTO> refreshRemainRecordDTOList) {

        urlRecordService.refreshRecordRemainTimes(refreshRemainRecordDTOList);
    }

    /**
     * 参数检查并且设置默认值(如果需要)
     *
     * @param dataModel
     */
    private void dataModelCheckAndSetDefault(DataModel dataModel) {
        if (dataModel == null) {
            log.error("保存数据为空！");
            throw new NullPointerException("保存参数不能为空！");
        }

        String originUrl = dataModel.getOriginUrl();
        String shortKey = dataModel.getShortKey();
        if (StrUtil.isBlank(originUrl) || StrUtil.isBlank(shortKey)) {
            log.error("原始url:{},短链ID:{},均不能为空！", originUrl, shortKey);
            throw new IllegalArgumentException("生成短链参数异常！");
        }
        Long times = dataModel.getTimes();
        // 次数为空的话设置未不限次数标识(-1) 小于-1也设置为-1
        times = times == null ? -1 : times < -1 ? -1 : times;
        LocalDateTime expireTime = dataModel.getExpireTime() == null ? DateUtil.halfYearAfter() : dataModel.getExpireTime();
        dataModel.setTimes(times);
        dataModel.setExpireTime(expireTime);
        boolean updateTimes = dataModel.getUpdateTimes() == null || dataModel.getUpdateTimes();
        dataModel.setUpdateTimes(updateTimes);
    }
}
